package  com.liuyc.lib.tools

import android.os.Handler
import android.os.Looper
import androidx.arch.core.executor.ArchTaskExecutor
import androidx.arch.core.executor.DefaultTaskExecutor
import androidx.lifecycle.*
import java.util.concurrent.ConcurrentHashMap

/**
 * LiveDataBus
 * 取代 EventBus 可以实现事件分发，并且可以设置是否使用粘性事件。
 * 因为应用LiveData 所以可以在宿主 非显示状态的时候 不发出数据通知
 * 宿主触发 onDestroyed  后自动解绑
 * 刘隽
 */
object LiveDataBus {
    /**
     * 存放事件集合
     */
    private val eventMap = ConcurrentHashMap<String, Any>()

    private val mHandler: Handler by lazy {
        Handler(Looper.getMainLooper())
    }

    val DEFALUT_VERSION = 0;

    /**
     * 使用class 比 string 更不容易出现重叠情况。
     * 我们在存储的时候并不会存储 class 只会存储 一个完全 class 类名称
     */
    fun <T> with(clazz: Class<*>): StickyLiveData<T> {
        var eventKey = clazz.name
        var liveData = eventMap[eventKey]
        if (liveData == null) {
            liveData = StickyLiveData<T>(eventKey)
            eventMap.put(eventKey, liveData)
        }

        return liveData as StickyLiveData<T>
    }

    /**
     * 类似于 liveData
     */
    class StickyLiveData<T>(private val eventKey: String) : LiveData<T>() {
        //分发数据对应的 版本
        internal var mVersion: Int = DEFALUT_VERSION

        /**
         * 主线程内 使用 这个方法来通知事件，非主进程会抛出异常
         */
        public override fun setValue(value: T) {
            //统计我们自己的数据库版本
            mVersion++;
            super.setValue(value)
        }

        /**
         * 在异步线程 调用 事件 通知理论上
         */
        public override fun postValue(value: T) {

            if (Looper.myLooper() == Looper.getMainLooper()){
                setValue(value)
            }else{
                //我们自己 模拟 liveData 中的 与 postValue 不一样我们去除了
                mHandler.post(SetValueRunnable.create(this, value))

                super.postValue(value)
            }
        }

        /**
         * 设置非 粘性的观察者
         */
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            observerSticky(owner, false, observer)
        }

        /**
         * 设置粘性观察者
         */
        fun observerSticky(owner: LifecycleOwner, isSticky: Boolean, observer: Observer<in T>) {
            // 增加 宿主状态监听，如果宿主已经 触发 onDestroy 后则需要移除观察者
            owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
                if (event == Lifecycle.Event.ON_DESTROY) {
                    eventMap.remove(eventKey)
                }
            })
            //这边继续走 liveData的 逻辑
            super.observe(owner, StickyObserver(this, isSticky, observer))
        }
    }

    /**
     * 我们将我们传递给 liveData Observer 重新封装一次
     * 直接使用我们自己的 version 配合 isSticky 判断 是否使用 粘性事件
     */
    class StickyObserver<T>(
            val stickyLiveData: StickyLiveData<T>,
            val isSticky: Boolean,
            val observer: Observer<in T>
    ) : Observer<T> {

        private var lastVersion: Int = DEFALUT_VERSION;

        init {
            //如果是 非粘性的观察者，则需要在一开始的时候 将 整个事件的 version 赋予观察者
            if (!isSticky) lastVersion = stickyLiveData.mVersion
        }

        /**
         * 这里会先在 LiveData considerNotify 方法中触发
         * 这时候我们可以根据 liveData 的方案来更新我们直接的 version
         */
        override fun onChanged(t: T) {
            //由于是自己来实现 version的控制，那么我们在change的时候 需要复写 version的变化比较
            if (lastVersion >= stickyLiveData.mVersion) {
                return
            }

            lastVersion = stickyLiveData.mVersion
            observer.onChanged(t)

        }
    }


    private class SetValueRunnable<T> private constructor(private val liveData: StickyLiveData<T>, private val data: T) : Runnable {

        companion object{
            fun <T> create(liveData: StickyLiveData<T>, data: T): SetValueRunnable<T>{
                return SetValueRunnable(liveData, data)
            }
        }

        override fun run() {
            liveData.setValue(data)
        }
    }

}